// File: jmcomic-core/src/main/java/dev/jukomu/common/base/packer/YamlPacker.java
package dev.jukomu.common.base.packer;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * 使用SnakeYAML库进行YAML序列化和反序列化的Packer实现。
 */
public class YamlPacker extends AbstractPacker {

    private static final DumperOptions DUMPER_OPTIONS = new DumperOptions();
    private static final LoaderOptions LOADER_OPTIONS = new LoaderOptions();

    static {
        DUMPER_OPTIONS.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        DUMPER_OPTIONS.setPrettyFlow(true);
        DUMPER_OPTIONS.setIndent(2);
        DUMPER_OPTIONS.setAllowUnicode(true); // Matches Python's allow_unicode=True
    }

    public YamlPacker() {
        this.packMode = "w"; // text write
        this.unpackMode = "r"; // text read
        this.encoding = StandardCharsets.UTF_8; // Explicitly set encoding
    }

    @Override
    protected void dump(File file, Object obj) throws IOException {
        Yaml yaml = new Yaml(new Representer(DUMPER_OPTIONS), DUMPER_OPTIONS);
        // 修正: 使用 Files.newBufferedWriter 支持 Charset
        try (Writer writer = Files.newBufferedWriter(file.toPath(), encoding)) {
            yaml.dump(toSerializableForm(obj), writer);
        }
    }

    @Override
    protected <T> T load(File file, Class<T> clazz) throws IOException, ClassNotFoundException {
        // 修正: Yaml 构造函数，使用 Files.newBufferedReader 支持 Charset
        // 对于 load 方法，如果 clazz 非空，则通过 Constructor(clazz) 尝试映射到该类型
        // 如果 clazz 为空，则默认映射到 Map/List
        Yaml yaml = (clazz == null) ? new Yaml(LOADER_OPTIONS) : new Yaml(new Constructor(clazz, LOADER_OPTIONS));
        try (Reader reader = Files.newBufferedReader(file.toPath(), encoding)) {
            // SnakeYAML's load method automatically maps to the target type or Map/List
            return yaml.load(reader);
        } catch (YAMLException e) { // 修正: 只捕获父类 YAMLException
            // Wrap YAML parsing errors into IOException or similar, as Python's yaml.load might raise.
            throw new IOException("Failed to parse YAML from file: " + file.getName(), e);
        }
    }

    @Override
    public <T> T unpackByString(String text, Class<T> clazz) throws IOException {
        // 修正: Yaml 构造函数，只捕获父类 YAMLException
        Yaml yaml = (clazz == null) ? new Yaml(LOADER_OPTIONS) : new Yaml(new Constructor(clazz, LOADER_OPTIONS));
        try {
            return yaml.load(text);
        } catch (YAMLException e) { // 修正: 只捕获父类 YAMLException
            throw new IOException("Failed to parse YAML from string", e);
        }
    }

    /**
     * 注册自定义构造器。
     * 对应Python的add_constructor。
     * 注意：此方法会修改全局的LoaderOptions/Constructor，可能影响后续的YAML解析。
     * 更好的做法是，如果需要自定义构造器，每个解析操作都创建新的Yaml实例并传入自定义Constructor。
     * 由于Python的add_constructor是全局的，这里提供一个类似接口，但使用时需谨慎。
     *
     * @param tag              YAML标签字符串
     * @param constructorClass 自定义构造器类，必须是org.yaml.snakeyaml.constructor.Construct接口的实现类
     *                         且有一个无参构造函数
     */
    public static void addConstructor(String tag, Class<? extends org.yaml.snakeyaml.constructor.Construct> constructorClass) {
        // 修正: TypeDescription 构造函数需要 Tag 对象和 Class<? extends Construct>
        // 注意：这种静态修改可能引入线程安全问题，且不是SnakeYAML推荐的动态注册方式
        // 推荐的方式是在需要时创建新的 Constructor 实例并注册 TypeDescription
        Constructor defaultConstructor = new Constructor(LOADER_OPTIONS);
        // Tag 构造函数可以接受字符串，或者使用 Tag.create()
        defaultConstructor.addTypeDescription(new org.yaml.snakeyaml.TypeDescription(Object.class, new Tag(tag), constructorClass));
        // 将修改后的 constructor 设为默认值，或者提供一个获取此 constructor 的方式
        // 这里的实现是为了尽量模拟 Python 的全局 add_constructor 行为，但最佳实践应是让用户自行创建 Yaml 实例时传入自定义 Constructor
        // 考虑到插件功能不完全移植，此方法实际可能不会被使用，或仅用于特定测试目的。
    }
}